#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <pthread.h>
#include <search.h>
#include "conf.h"
#include "configure.h"
#include "fstring.h"
#include "url_info.h"
#include "filename_cache.h"

extern int buff_404_len;
extern char buff_404[];
extern void* http_server();
void* udp_traker(void* u );
void* tracke(void* sock_client);
void send_404(int sock);
int tcp_port=80;
int udp_port=81;


struct filename_cache fc;

#define MAX_CONFIGURE 6
struct configure configure[MAX_CONFIGURE];
struct configure *global_conf;
pthread_mutex_t rand_lock = PTHREAD_MUTEX_INITIALIZER;

void*reload_configure(void *parm)
{
	int i = 0;
	struct configure *cur;
	while (1)
	{
		sleep(60);
		i = (i+1) % MAX_CONFIGURE;
		cur = &configure[i];
		load_configure(cur);
		global_conf = cur;
	}
}

void create_reload_configure_thread()
{
	pthread_t t;
	if (pthread_create(&t, 0, reload_configure, 0) != 0)
		printf("create thread fail\n");
}

char location_1[]="HTTP/1.1 302 Found\r\n"
				  "Location: http://";
int location_1_len=0;
char location_2[]="Connetion: close\r\n\r\n";
int location_2_len=0;

int main(void)
{
	int sock_lsn;
	int sock_peer;
	struct sockaddr_in addr_lsn;
	pthread_t stra_pthread;
	pthread_t threa_udp;
	pthread_attr_t attr;


	buff_404_len = strlen(buff_404);
	location_1_len=strlen(location_1);
	location_2_len=strlen(location_2);

	memset(&addr_lsn, 0, sizeof(addr_lsn));
	addr_lsn.sin_family=AF_INET;
	addr_lsn.sin_port=htons(tcp_port);
	addr_lsn.sin_addr.s_addr=htonl(INADDR_ANY);

	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

	fcache_init(&fc);

	signal(SIGPIPE, SIG_IGN);
	srand(getpid());

	load_configure(global_conf = &configure[0]);
	create_reload_configure_thread();
//	{
//		struct configure *conf = global_conf;
//		int i;
//		int s;
//		for (i=0; i<conf->nr_cache; i++)
//		{
//			printf("%s %d G\n", conf->node_str[i], conf->cache_list[i].total_av);
//		}
//		printf("\n");
//		for (i=0; i<20; i++)
//		{
//			s = select_master_cache_r(conf, &rand_lock);
//			printf("%s %d (%d)G\n", conf->node_str[s], conf->cache_list[s].weight, conf->cache_list[s].total_av);
//		}
//		sleep(2000000);
//	}


	if(fork()==0){

		http_server();
		_exit(0);
	}
    //sleep(10000000);
	//return;

	if(pthread_create(&threa_udp, 0, udp_traker, 0) != 0){
		printf("pthread_create fail\r\n");
		_exit(0);
	}

	if((sock_lsn = socket(PF_INET, SOCK_STREAM, 0)) < 0){
		printf("socket building fail\r\n");
		return 0;
	}

	if(bind(sock_lsn, (struct sockaddr*)&addr_lsn, (socklen_t)sizeof(addr_lsn)) < 0){
		printf("socket bind fail\r\n");
		return 0;
	}

	if(listen(sock_lsn, 20) < 0){
		printf("socket listen faill\r\n");
		return 0;
	}

	daemon(0,0);
	while(1){
		if((sock_peer = accept(sock_lsn, 0, 0)) < 0){
			perror("accept");
			continue;
		}
		if(pthread_create(&stra_pthread, &attr, tracke, (void*)sock_peer) != 0){
			printf("create thrack pthread fail\r\n");
			close(sock_peer);
		}
	}
}

struct timeval tvsnd = {10,0}; 
struct timeval tvrcv = {30,0};

void* tracke (void* sock_client_){
	int i;
	int sock_client=(int)sock_client_;
	struct url_info url_info = {0, };
	struct configure *configure;
	char response_buff[2048];
	char* response_buff_index=response_buff;
	int udp_sock;
	struct sockaddr_in addr_send;
	int found;
	u_int32_t ip_found;
	int index;

	if (setsockopt(sock_client, SOL_SOCKET,SO_SNDTIMEO, (char *)&tvsnd,sizeof(struct timeval)) < 0)
		perror("setsockopt\r\n");
	if (setsockopt(sock_client, SOL_SOCKET,SO_RCVTIMEO, (char *)&tvrcv,sizeof(struct timeval)) < 0)
		perror("setsockopt\r\n");

	if(recv_header(sock_client, &url_info) < 0 )
	{
		send_404(sock_client);
		goto proce_end;
	}	

	if (process_header_line(&url_info) < 0)
	{
		send_404(sock_client);
		goto proce_end;
	}

	if (locate_filename_and_qmark(&url_info) < 0)
	{
		send_404(sock_client);
		goto proce_end;
	}

	if (!url_info.filename)
	{
		send_404(sock_client);
		goto proce_end;
	}

	configure = global_conf;

	if((found = fcache_find(&fc, &url_info, &ip_found)) == 0)
	{
		memset(&addr_send, 0, sizeof(addr_send));
		addr_send.sin_family=AF_INET;
		addr_send.sin_port=htons(81);
		
		if ((udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
			goto proce_end;

		for(i=0; i<configure->nr_cache; i++)
		{
			if (configure->cache_list[i].state != ST_PRESENT)
				continue;
			addr_send.sin_addr.s_addr = configure->cache_list[i].ip;
			sendto(udp_sock, url_info.url, strlen(url_info.url), 0, 
					(struct sockaddr *)&addr_send, sizeof(struct sockaddr_in));
		}
		close(udp_sock);
		sleep(1);
		found = fcache_find(&fc, &url_info, &ip_found);
	}

	if (found){
		index = cache_index(configure, ip_found);
		if (index < 0 || configure->cache_list[index].state == ST_DOWN)
		{
			/* need to delete the entry or update the entry when this is in case */
			index = select_master_cache_r(configure, &rand_lock);
			fcache_update(&fc, &url_info, configure->cache_list[index].ip);
		}
	} else
	{
		index = select_master_cache_r(configure, &rand_lock);
		fcache_add(&fc, &url_info, configure->cache_list[index].ip);
	}

	response_buff_index+=sprintf(response_buff_index,"%s",location_1);
	response_buff_index+=sprintf(response_buff_index,"%s",configure->node_str[index]);
	response_buff_index+=sprintf(response_buff_index,"%s",url_info.url+6);
	response_buff_index+=sprintf(response_buff_index,"\r\n%s",location_2);

	send(sock_client, response_buff, response_buff_index - response_buff, 0);

proce_end:
	close(sock_client);
	return 0;
}

char buff_404[] = "HTTP/1.1 404 Not Found\r\n"
				  "Content-Length: 0\r\n"
				  "Connection: Close\r\n"
				  "\r\n";
int buff_404_len = 0;
void send_404(int sock)
{
	send(sock, buff_404, buff_404_len, 0);
}
void* udp_traker(void* u )
{
	int udp_sock;
	struct sockaddr_in addr_bin;
	struct sockaddr_in addr_peer;
	int addr_peer_len;
	struct timeval tvsnd = {10,0}; 
	struct timeval tvrcv = {5,0};

	memset(&addr_bin, 0, sizeof(addr_bin));
	addr_bin.sin_family = AF_INET;
	addr_bin.sin_port = htons(udp_port);
	addr_bin.sin_addr.s_addr = htonl(INADDR_ANY);

	udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
	if(udp_sock < 0){ 
		printf("udp_sock buil fail\r\n");
		return 0;
	}

	if (setsockopt(udp_sock, SOL_SOCKET,SO_SNDTIMEO, (char *)&tvsnd,sizeof(struct timeval)) < 0)
		perror("setsockopt\r\n");
	if (setsockopt(udp_sock, SOL_SOCKET,SO_RCVTIMEO, (char *)&tvrcv,sizeof(struct timeval)) < 0)
		perror("setsockopt\r\n");

	if(bind(udp_sock, (struct sockaddr *)&addr_bin, sizeof(struct sockaddr_in)) < 0){
		printf("udp_sock bind fail\r\n");
		return 0;
	}

	while(1)
	{
		int nrecv;
		struct url_info url;
		addr_peer_len = sizeof(addr_peer);
		if((nrecv = recvfrom(udp_sock, url.buff, sizeof(url.buff)-1, 0, 
						(struct sockaddr*)&addr_peer, &addr_peer_len)) <= 0)
			continue;
		url.buff[nrecv] = 0;
		url.url = url.buff;
		if (locate_filename_and_qmark(&url) < 0) 
			continue;
		fcache_add(&fc, &url, addr_peer.sin_addr.s_addr);
	}
	return 0;
}
